package org.simp.core.body.decoder;

import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.simp.annotation.SimpBodyMsg;
import org.simp.common.Constants;
import org.simp.core.body.SimpBody;
import org.simp.core.body.SimpleSimpBody;
import org.simp.core.head.SimpHeader;
import org.simp.exception.SimpInitException;
import org.simp.exception.SimpNotFoundException;
import org.simp.util.ClassFilter;
import org.simp.util.ClassUtil;
import org.simp.util.PackUtil;

public class DefaultSimpBodyDecoder implements SimpBodyDecoder {

	private static final Logger log = Logger.getLogger(DefaultSimpBodyDecoder.class);
	
	public DefaultSimpBodyDecoder(String scanPackName ){
		this.scanPackName = scanPackName;
	}
	private String scanPackName;
	private Map<Integer ,Class<? extends SimpBody>> simpBodyMap = Collections.synchronizedMap(new HashMap<Integer,Class<? extends SimpBody>>());
	
	public static DefaultSimpBodyDecoder createDecoder(String scanPackName){
		DefaultSimpBodyDecoder decoder = new DefaultSimpBodyDecoder(scanPackName);
		decoder.initDecoder();
		return decoder;
	}
	@Override
	public void initDecoder() {
		log.debug("init body decoder ");
//		List<Class> classSet = ClassUtil.scanPackage(scanPackName);//PackUtil.getClasses(scanPackName);
		List<Class> classSet = ClassUtil.scanPackage(scanPackName, new ClassFilter() {
			
			@Override
			public boolean accept(Class clazz) {
				Annotation msg = clazz.getAnnotation(SimpBodyMsg.class);
				return msg !=null;
			}
		});
		Set<Class> set =  new HashSet<Class>();
		for(Class clazz : classSet){
			set.add(clazz);
		}
		for(Class clazz : set){
			if(!SimpBody.class.isAssignableFrom(clazz) || clazz.isInterface()){
				continue ;
			}
			injectBody(clazz);
		}
		
	}
	
	private void injectBody(Class<?> clazz){
		Class<?> [] interfaces = clazz.getInterfaces();
		SimpBodyMsg msg = clazz.getAnnotation(SimpBodyMsg.class);
		if(msg != null){
			short mainId = msg.mainId();
			short moudleId = msg.moudleId();
			int requestId = ((mainId << 16) & 0xffffffff) | (moudleId & 0xffff);
			addMap(requestId, (Class<? extends SimpBody>) clazz);
		}
	}

	private void addMap(Integer requestId,Class<? extends SimpBody> clazz){
		if(simpBodyMap.containsKey(requestId)){
			throw new SimpInitException(" simpBody : mainId :" + ((requestId >>16) & 0xffff) + " moudleId : " + (requestId & 0xffff) +" has register ");
		}else{
			simpBodyMap.put(requestId, clazz);
		}
	}
	@Override
	public SimpBody decoder(SimpHeader head, byte[] bodyData) throws SimpNotFoundException  {
		SimpBody simpBody = getSimpBodyByHead(head);
		if(simpBody !=null){
			simpBody.decode(bodyData);
		}
		return simpBody;
	}

	@Override
	public SimpBody getSimpBodyByHead(SimpHeader head) throws SimpNotFoundException  {
		int requestId = head.getRequestId();
		SimpBody body = getSimpBodyById(requestId);
		if(body == null){
			/**如果消息头部中ContentType为simple，该协议没有在消息体上注解过，使用简单消息体进行解析**/
			if(Constants.SIMPLE_CONTENT_TYPE .equalsIgnoreCase(head.getHeadParam(Constants.CONTENT_TYPE))){
				body = new SimpleSimpBody();
			}else{
				throw new SimpNotFoundException(head.getMainId(), head.getMoudleId());
			}
		}
		return body;
	}

	private SimpBody getSimpBodyById(int requestId){
		if(!simpBodyMap.containsKey(requestId)){
			return null;
		}
		Class<? extends SimpBody> clazz = simpBodyMap.get(requestId);
		try{
			SimpBody body  = clazz.newInstance();
			return body;
		}catch (Exception e) {
			log.error(" SimpBody init error ",e);
			return null;
		}
	}
	public void registerBody(Class<?> clazz){
		injectBody(clazz);
	}
}
